package user

import (
	"context"
	"mychat/api"
	"mychat/internal/common/cmctx"
	"mychat/internal/common/errors"
	"mychat/internal/common/jwt"
	"mychat/internal/common/logger"
	"mychat/internal/common/utils"
	"mychat/internal/conf"
	"mychat/internal/data"
	"mychat/internal/data/dao/model"
	"time"
)

type UserService interface {
	CreateUser(ctx context.Context, req *api.CreateUserRequest) (*api.CreateUserReply, *errors.Error)
	Login(ctx context.Context, req *api.UserLoginRequest) (*api.UserLoginReply, *errors.Error)
	Logout(ctx context.Context) (*api.UserLogoutReply, *errors.Error)

	ApplyFriend(ctx context.Context, req *api.ApplyFriendRequest) (*api.ApplyFriendReply, *errors.Error)
	ConfirmFriend(ctx context.Context, req *api.ConfirmFriendRequest) (*api.ConfirmFriendReply, *errors.Error)
	DeleteFriend(ctx context.Context, req *api.DeleteFriendRequest) (*api.DeleteFriendReply, *errors.Error)
}

type userService struct {
	bs       *conf.Bootstrap
	lg       logger.Logger
	data     *data.Data
	jwtToken struct {
		jwtSecret string
		expireSec time.Duration
	}
}

func NewUserService(bs *conf.Bootstrap, lg logger.Logger, dt *data.Data) UserService {
	return &userService{
		bs:   bs,
		lg:   lg.WithCustomStrTag("_user_service"),
		data: dt,
		jwtToken: struct {
			jwtSecret string
			expireSec time.Duration
		}{
			jwtSecret: bs.Server.Http.JwtSecret,
			expireSec: time.Duration(bs.Server.Http.TokenExpireSec) * time.Second,
		},
	}
}

// user
// @Summary 用户新建
// @Description
// @Tags user
// @Accept json
// @Param name body api.CreateUserRequest true "用户和密码"
// @Produce json
// @Success 200 {object} api.CreateUserReply
// @Failure 400 {object} errors.Error
// @Failure 404 {object} errors.Error
// @Router /v1/user/register [post]
func (s *userService) CreateUser(ctx context.Context, req *api.CreateUserRequest) (*api.CreateUserReply, *errors.Error) {

	user, err := s.data.UserDao.GetUserWithName(ctx, req.Name)
	if err != nil {
		s.lg.Error(ctx, "UserDao.CreateUser failed||userName=%v||err=%v",
			req.Name, err)
		return nil, &errors.DefaultError
	}

	if user != nil {
		s.lg.Error(ctx, "user already exist||user=%v", user)
		return nil, &errors.UserAlreadyExistError
	}

	user = &model.User{
		Name:   req.Name,
		Passwd: utils.Encrypt(req.Passwd),
	}

	err = s.data.UserDao.CreateUser(ctx, user)
	if err != nil {
		s.lg.Error(ctx, "UserDao.CreateUser failed||userName=%v||err=%v",
			req.Name, err)
		return nil, &errors.UserCreateError
	}

	token, err := jwt.CreateToken(user.ID, s.jwtToken.jwtSecret, s.jwtToken.expireSec)
	if err != nil {
		s.lg.Error(ctx, "jwt.CreateToken failed||userId=%v||err=%v", user.ID, err)
		return nil, &errors.TokenCreateFailed
	}
	// save cache

	return &api.CreateUserReply{Token: token}, nil
}

// user
// @Summary 用户登录
// @Description
// @Tags user
// @Accept json
// @Param name body api.UserLoginRequest true "用户和密码"
// @Produce json
// @Success 200 {object} api.UserLoginReply
// @Failure 400 {object} errors.Error
// @Failure 404 {object} errors.Error
// @Router /v1/user/login [post]
func (s *userService) Login(ctx context.Context, req *api.UserLoginRequest) (*api.UserLoginReply, *errors.Error) {
	user, err := s.data.UserDao.GetUserWithName(ctx, req.Name)
	if err != nil {
		s.lg.Error(ctx, "GetUserWithName failed||name=%v||err=%v", req.Name, err)
		return nil, &errors.DefaultError
	}

	if user == nil {
		return nil, &errors.UserInvalidError
	}

	token, err := jwt.CreateToken(user.ID, s.jwtToken.jwtSecret, s.jwtToken.expireSec)
	if err != nil {
		s.lg.Error(ctx, "CreateToken failed||userId=%v||err=%v", user.ID, err)
		return nil, &errors.TokenCreateFailed
	}
	// set cache

	return &api.UserLoginReply{Token: token}, nil
}

// user
// @Summary 用户登出
// @Description
// @Tags user
// @Accept json
// @Produce json
// @Success 200 {object} api.UserLoginReply
// @Failure 400 {object} errors.Error
// @Failure 404 {object} errors.Error
// @Router /v1/user/logout [put]
func (s *userService) Logout(ctx context.Context) (*api.UserLogoutReply, *errors.Error) {
	// delete cache
	return &api.UserLogoutReply{LogoutAt: time.Now().Unix()}, nil
}

// friend
// @Summary 好友申请
// @Description 应该通过ws请求
// @Tags friend
// @Accept json
// @Param name body api.ApplyFriendRequest true "目标id"
// @Produce json
// @Success 200 {object} api.ApplyFriendReply
// @Failure 400 {object} errors.Error
// @Failure 404 {object} errors.Error
// @Router /v1/friend/apply [post]
func (s *userService) ApplyFriend(ctx context.Context, req *api.ApplyFriendRequest) (*api.ApplyFriendReply, *errors.Error) {
	userId := cmctx.GetCtxUserId(ctx)
	ufriend, err := s.data.FriendDao.GetUserFriend(ctx, userId, uint(req.TargetId))
	if err != nil {
		s.lg.Error(ctx, "FriendDao.GetUserFriend failed||userId=%v||friendId=%v||err=%v",
			userId, req.TargetId, err)
		return nil, &errors.DefaultError
	}

	if ufriend != nil {
		return &api.ApplyFriendReply{
			IsFriend:  true,
			ApplyedAt: ufriend.RelatedAt.Unix(),
		}, nil
	}

	// 给用户发送好友申请
	// TODO:
	return &api.ApplyFriendReply{IsFriend: false, ApplyedAt: time.Now().Unix()}, nil
}

// friend
// @Summary 同意好友申请
// @Description 应该通过ws请求
// @Tags friend
// @Accept json
// @Param name body api.ConfirmFriendRequest true "目标id"
// @Produce json
// @Success 200 {object} api.ConfirmFriendReply
// @Failure 400 {object} errors.Error
// @Failure 404 {object} errors.Error
// @Router /v1/friend/confirm [post]
func (s *userService) ConfirmFriend(ctx context.Context, req *api.ConfirmFriendRequest) (*api.ConfirmFriendReply, *errors.Error) {
	userId := cmctx.GetCtxUserId(ctx)
	ufriend, err := s.data.FriendDao.GetUserFriend(ctx, userId, uint(req.TargetId))
	if err != nil {
		s.lg.Error(ctx, "FriendDao.GetUserFriend failed||userId=%v||friendId=%v||err=%v",
			userId, req.TargetId, err)
		return nil, &errors.DefaultError
	}

	if ufriend != nil {
		return &api.ConfirmFriendReply{
			ConfirmedAt: ufriend.RelatedAt.Unix(),
		}, nil
	}

	err = s.data.FriendDao.AddUserFriend(ctx, userId, uint(req.TargetId))
	if err != nil {
		s.lg.Error(ctx, "FriendDao.AddUserFriend failed||userId=%v||friendId=%v||err=%v",
			userId, req.TargetId, err)
		return nil, &errors.FriendAddError
	}

	// 缓存加载好友信息

	return &api.ConfirmFriendReply{ConfirmedAt: time.Now().Unix()}, nil
}

// friend
// @Summary 移除好友
// @Description 应该通过ws请求
// @Tags friend
// @Accept json
// @Param name body api.DeleteFriendRequest true "目标id"
// @Produce json
// @Success 200 {object} api.DeleteFriendReply
// @Failure 400 {object} errors.Error
// @Failure 404 {object} errors.Error
// @Router /v1/friend/remove [delete]
func (s *userService) DeleteFriend(ctx context.Context, req *api.DeleteFriendRequest) (*api.DeleteFriendReply, *errors.Error) {
	userId := cmctx.GetCtxUserId(ctx)
	ufriend, err := s.data.FriendDao.GetUserFriend(ctx, userId, uint(req.TargetId))
	if err != nil {
		s.lg.Error(ctx, "FriendDao.GetUserFriend failed||userId=%v||friendId=%v||err=%v",
			userId, req.TargetId, err)
		return nil, &errors.DefaultError
	}

	if ufriend == nil {
		return &api.DeleteFriendReply{
			DeletedAt: time.Now().Unix(),
		}, nil
	}

	err = s.data.FriendDao.DeleteUserFriend(ctx, userId, uint(req.TargetId))
	if err != nil {
		s.lg.Error(ctx, "FriendDao.UpsertUserFriend failed||targetId=%v||err=%v",
			req.TargetId, err)
		return nil, &errors.DefaultError
	}

	// delete清理缓存

	return &api.DeleteFriendReply{DeletedAt: time.Now().Unix()}, nil
}
